#include "HighLightTextViewer.h"

#include <QApplication>
#include <QScrollBar>

/******************************  settings *************************************/

/* background color when in edit mode */
#define  EDIT_MODE_BACKGROUND    "#e0f0ff"    //QColor( 235, 250, 225)

/** @{ active block highlight */
#define  SCRIPT_HIGHLIGHT_BG     QColor(255, 255, 50)
#define  SCRIPT_HIGHLIGHT_TEXT   QColor(200, 200, 255)
/** @} */

/** macro for 'edit mode' aspect*/
#define EDIT_MODE_STYLESHET   (QString(" QTextEdit { background:") + EDIT_MODE_BACKGROUND + "; } ")

/*******************************************************************************/

HighLightTextViewer::HighLightTextViewer(QWidget *parent) :
   QTextBrowser(parent)
{
   m_cursor = new QTextCursor(document());
   m_currentBlock = document()->begin();
   m_editMode  = false;

   resetStyle();
   setUndoRedoEnabled( true );
   setOpenLinks( false);

   setContextMenuPolicy( Qt::ActionsContextMenu);

   /* this avoids a scrollbar flickering problem */
   setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );

   setSearchPaths( QStringList(qApp->applicationDirPath() + "/res/images/") );

   connect( &m_filteredMoveTimer, SIGNAL(timeout()),
            this, SLOT(onFilteredMoveTimerExpired()) );
}

void HighLightTextViewer::onActivateNextBlock()
{
   activateBlock( calculateNextBlock());
}

int HighLightTextViewer::getActiveBlockLength() const
{
   return m_currentBlock.length();
}

void HighLightTextViewer::getScriptContent(QString &content) const
{
   content = document()->toHtml();
}

void HighLightTextViewer::setScriptContent(const QString &content)
{
   document()->setHtml( content);
   setCurrentBlock( document()->begin());
   scrollToActiveBlock();
}

/**
 * give a default value to style variables
 */
void HighLightTextViewer::resetStyle()
{
   m_textHighLight = SCRIPT_HIGHLIGHT_TEXT;
   m_bgHighLight   = SCRIPT_HIGHLIGHT_BG;

   /* update view */
   highlightActiveBlock();
}


void HighLightTextViewer::activateBlock( const QTextBlock & block)
{
   /* clear-update-highlight sequence */
   activeBlockClearHighlight();
   setCurrentBlock( block );
   highlightActiveBlock();

   scrollToActiveBlock();
}

void HighLightTextViewer::mouseMoveEvent(QMouseEvent *)
{
   m_filteredMoveTimer.start( 100);
}

void HighLightTextViewer::resizeEvent(QResizeEvent * event)
{
   QTextBrowser::resizeEvent( event );

   /* update scrolling position only in read mode */
   if( m_editMode == false )
   {
      scrollToActiveBlock();
   }
}

void HighLightTextViewer::mouseDoubleClickEvent( QMouseEvent * event )
{
   if( m_editMode == true )
   {
      /* in edit mode, use default behaviour */
      QTextEdit::mouseDoubleClickEvent( event);
   }
   else
   {
      /* 'show mode' behaviour */
      QTextCursor currentCursor= cursorForPosition(event->pos());
      currentCursor.movePosition( QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);

      activateBlock( currentCursor.block());
      /* base handler is not called to avoid a spurious selection */
   }
}

/**
 * When parameter \p edit is true, editing text is allowed and
 * background changes to make this evident.
 * If \p edit is false, editor is in 'show mode', with auto-
 * scroll allowed, (if enabled).
 */
void HighLightTextViewer::setEditMode(bool edit)
{
   QString styleSheet = edit ? EDIT_MODE_STYLESHET : QString();

   m_editMode = edit;
   setStyleSheet( styleSheet);
   setReadOnly( !edit);

   if (edit)
   {
      activeBlockClearHighlight();
   }
   else
   {
      highlightActiveBlock();
   }
}

void HighLightTextViewer::setHtml( const QString & text )
{
   QTextEdit::setHtml(text);

   // set the begin of new file as active block
   delete m_cursor;
   m_cursor = new QTextCursor(document());
   m_currentBlock = document()->begin();

   highlightActiveBlock();
}

void HighLightTextViewer::onFilteredMoveTimerExpired()
{
   m_filteredMoveTimer.stop();

   /* filtered hover event is sent only when not in edit mode */
   if (m_editMode == false)
   {
      QPoint currentMousePosition = mapFromGlobal( QCursor::pos() );
      emit filteredMouseMoveEvent( currentMousePosition);
   }
}

/**
 * set highlight background for current block
 */
void HighLightTextViewer::highlightActiveBlock()
{
   QList<QTextEdit::ExtraSelection> extraSelections;
   QTextEdit::ExtraSelection selection;

   QColor lineColor(m_bgHighLight);

   selection.format.setBackground(lineColor);
   selection.format.setProperty(QTextFormat::FullWidthSelection, true);
   selection.cursor = QTextCursor(*m_cursor);
   selection.cursor.movePosition( QTextCursor::NextBlock, QTextCursor::KeepAnchor);
   extraSelections.append(selection);

   setExtraSelections(extraSelections);
}

/**
 * update current block and set position accordingly
 */
void HighLightTextViewer::setCurrentBlock(const QTextBlock &block)
{
   m_currentBlock = block;
   m_cursor->setPosition( m_currentBlock.position() );
}

/**
 * remove highlighted background for current block
 */
void HighLightTextViewer::activeBlockClearHighlight()
{
   setExtraSelections(QList<QTextEdit::ExtraSelection>());
}

/**
 *  return the block following to the active one
 */
QTextBlock HighLightTextViewer::calculateNextBlock()
{
   QTextBlock new_block;

   // return next block, if valid
   new_block = m_currentBlock.next();

   if( !(new_block.isValid()) )
   {
      new_block = document()->begin();
   }

   return new_block;
}

/**
 * adjust vertical scrollbar such that active block
 * appares in the middle of viewport
 */
void HighLightTextViewer::scrollToActiveBlock()
{
   int y_wport, yScroll_val, vMin, vMax;

   // scroll to selection
   y_wport = cursorRect( *m_cursor ).top();
   yScroll_val = verticalScrollBar()->value();
   yScroll_val += (y_wport - (viewport()->height() / 2) );

   // check scroll ranges
   vMin = verticalScrollBar()->minimum();
   if( yScroll_val < vMin)
   {
      yScroll_val = vMin;
   }

   vMax = verticalScrollBar()->maximum();
   if( yScroll_val > vMax)
   {
      yScroll_val = vMax;
   }

   verticalScrollBar()->setValue( yScroll_val );
}
